Lesson 7: Starting a Spell in JASS
Overview


Welcome to the next lesson. The basics have pretty much been covered, and now you'll probably want to use JASS to create spells or something. In this lesson, we'll go through the process of making a spell similar to the Omnislash spell found in DotA. (I've never actually seen the spell, but I know somewhat of how it works) This will show you some basic things used in JASS and room will be left for you to be able to modify the spell later on.


Starting Off


Before making a spell, you'll have to consider how it will work, what is happening step by step. First off, we need the trigger to go off when a unit casts a certain spell, right? And then we can begin thinking of the actions. In the spell I'm thinking off, the hero moves around in an area hitting a certain amount of units, but never the same unit twice. If you're looking at the spell being cast in-game, it might seem confusing, it might not. You have to break things down little by little. Here's some code for now that will start us off and I'll add to it as the lesson progresses:

function Slash_Condition takes nothing returns boolean

return GetSpellAbilityId() == 'A000' //Compares the ability id of the ability being cast to the ability id of our Slash spell.

endfunction

function Slash_Actions takes nothing returns nothing
endfunction

function InitTrig_Slash takes nothing returns nothing
set gg_trg_Slash = CreateTrigger() //Set the global variable that is created for our trigger to a new trigger

call TriggerRegisterAnyUnitEventBJ(gg_trg_Slash, EVENT_PLAYER_UNIT_SPELL_EFFECT) //This BJ function registers an event for whenever a unit starts the effect
//of an ability for our trigger

call TriggerAddCondition(gg_trg_Slash, Condition(function Slash_Condition))
call TriggerAddAction(gg_trg_Slash, function Slash_Actions)
endfunction


So far all we have is the basic trigger set up that will go off whenever the right ability is cast. You need to make sure you've created an ability (preferably based off channel with the values set correctly, if you need to know more about channel, please ask). Now we will create the actual effects of the spell. Now you'll need to think "what happens in the spell". Here's what happens: the hero is moved around within a certain area, he will only be moved to enemies, for each unit he is moved to, he will deal damage, and at the end he will return to the spot that he started at. The first thing to do now is to create a local variable to everything we'll need to keep track of. Here's what I thought of: a unit variable for the hero, a location for where the hero starts at, a group to hold all the enemies the hero will hit, and another unit variable to temporarily hold picked units in. You might not get the point of the last variable, but I'll show you how it's used soon. Here's what we should have now:

function Slash_Actions takes nothing returns nothing

local unit caster //Will hold our caster
local location start_position //Holds the starting position of the hero
local group enemies //Will hold the unit group of enemies our hero will hit
local unit temp //Used for when going through the group (explained later)

endfunction


Now, if you've used GUI before, you'll probably be wanting to do something like "Pick Every Unit in (group) and do actions", but there's a problem with that. In order to use that function (it's know as ForGroup in JASS), you'll need a separate function that you'll be unable to pass any values to. Now you could use global variables to transfer information, but then the spell wouldn't be able to be used by more than 1 unit at a time. You could possibly think of other ways around this, but they'd take more time than it would be worth. I'll give you a hint on how I do it, I use a loop. "Loop?" you might be thinking, "how can I use a loop?" because if you've used GUI before, the loops would run for a fixed length (for integer # through #, do actions), unlike in JASS where a loop will only end when a certain condition is met. And that condition will be that all enemies in the enemy group have been hit. Here's some more code to hopefully explain a little bit more of what I mean:

function Slash_Actions takes nothing returns nothing
local unit caster = GetSpellAbilityUnit() //Create a local variable and set it to the unit that's casting the spell
local location start_position = GetUnitLoc(caster) //Create the local and set it to the caster's position
local group enemies = CreateGroup() //When you create a group in JASS, it's like a trigger. First you need it empty, and then you add stuff to it
local unit temp //Used when looping through the group
local integer count = 5 //This will be the maximum amount of enemies that can be hit

call GroupEnumUnitsInRangeOfLoc(enemies, start_position, 500.0, null)

// GroupEnumUnitsInRangeOfLoc takes a group, a location, a radius, and a boolexpr (a condition, kind of) and then gets units within that radius and adds them to our group

loop
set temp = FirstOfGroup(enemies) //FirstOfGroup returns a unit from a group in no particular order that I know of
exitwhen temp == null or count == 0

//The loop will stop when temp is null (meaning that there are no more units left in the group) or when we've hit 5 enemies

call GroupRemoveUnit(enemies, temp) //Removes the unit from the group so that it can't be picked again
endloop

//Cleaning up memory leaks will be put here

endfunction


In this section of code, we've set up some more basic parts to our code. We've initialized the variables and then filled the group with units within 500 range of the caster. Next, we start a loop and for each cycle in the loop, the temporary unit variable will be set to a unit from the group. Then we'll tell the loop to exit when the group has no units left in it or the caster has hit 5 enemies (count would be decreased for each enemy hit). At the end of each loop, the unit being used will be removed from the group so that it can't be picked again (and so prevents an infinite loop as well). Then we end the code executed by the loop and end the function.


The next step will now be to add in what the function will do for each unit picked. The first thing that you will have to notice is that the GroupEnumUnitsInRangeOfLoc grouped all units in 500 range of the hero's location, including allies and the hero. That means we'll have to add a simple condition to check whether or not the unit is an ally before performing additional actions. After that's done, what next? The unit will have to move to each enemy hit and damage them, right? Don't forget setting the count variable to count – 1 as well, or more enemies could be hit than desired. Before going on to the next bit of code, declare another location variable to hold the picked enemy's position, I'll go by the name of temp_loc. Here's what we'll have next for the loop

loop
set temp = FirstOfGroup(enemies)
exitwhen temp == null or count == 0

if IsUnitEnemy(temp, GetOwningPlayer(caster)) then //If the enemy unit is an enemy of the owner of the caster

set temp_loc = GetUnitLoc(temp) //Set a temporary location variable to the enemy position

call SetUnitPositionLoc(caster, temp_loc) //Move our unit instantly to the enemy's location

call UnitDamageTarget(caster, temp, 50, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)

//The caster will instantly damage the enemy for 50 damage with an attack type of chaos
//You can ignore the damage type, and the last parameter (weapontype) is just for sound, but we will
//have none for right now

set count = count - 1 //A unit has been hit, so now the units left to hit is reduced by 1

endif

call GroupRemoveUnit(enemies, temp)
endloop


Okay, this loop will now allow our unit to move to every unit within range and damage them. You might think we're done, right? Our hero will move to each unit in range and damage them, but you now have to ask yourself if we're missing anything. Are we?


Challenge


I bet you've already guessed the challenge, finish the spell! Don't worry about special effects (unless you want to), you just need to finish up on the basics that will happen and clean memory leaks. In the next lesson, the spell will be finished up and enhanced with special effects and more! Don't hesitate to post code on the forums if you're having trouble.

Good Luck! -wyrmlord